package model.util;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import model.StatechartComponent;

public class Line {

	public enum Property {
		START_AT, END_AT, INTERNAL_LOCATIONS, LINE_STYLE, START_ARROW_TYPE, END_ARROW_TYPE
	}

	private Logger log = LoggerFactory.getLogger(Line.class);

	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
			this);

	public enum ArrowType {
		NO_ARROW, OPEN_ARROW
	}

	public enum LineStyle {
		SOLID, DASH
	}

	private Point startAt;
	private Point endAt;
	private LinkedList<Point> internalLocations = new LinkedList<Point>();
	private LineStyle lineStyle;
	private ArrowType startArrowType;
	private ArrowType endArrowType;

	public Line(Point startAt, Point endAt) {
		this(startAt, endAt, ArrowType.NO_ARROW);
	}

	public Line(Point startAt, Point endAt, LinkedList<Point> internLocations) {
		this(startAt, endAt, null, LineStyle.SOLID, ArrowType.NO_ARROW,
				ArrowType.NO_ARROW);
	}

	public Line(Point startAt, Point endAt, ArrowType endArrowType) {
		this(startAt, endAt, null, LineStyle.SOLID, ArrowType.NO_ARROW,
				endArrowType);
	}

	public Line(Point startAt, Point endAt, LinkedList<Point> internLocations,
			ArrowType endArrowType) {
		this(startAt, endAt, internLocations, LineStyle.SOLID,
				ArrowType.NO_ARROW, endArrowType);
	}

	public Line(Point startAt, Point endAt, LinkedList<Point> internLocations,
			LineStyle lineStyle, ArrowType startArrowType,
			ArrowType endArrowType) {
		this.startAt = startAt;
		this.endAt = endAt;
		this.internalLocations = internLocations;
		this.lineStyle = lineStyle;
		this.startArrowType = startArrowType;
		this.endArrowType = endArrowType;
	}

	public void addPropertyChangeListener(
			StatechartComponent.Property property,
			PropertyChangeListener listener) {
		this.propertyChangeSupport.addPropertyChangeListener(property.name(),
				listener);
	}

	public void removePropertyChangeListener(
			StatechartComponent.Property property,
			PropertyChangeListener listener) {
		this.propertyChangeSupport.removePropertyChangeListener(
				property.name(), listener);
	}

	public void addInternalLocation(Point location, int index) {
		LinkedList<Point> oldInternalLocations = new LinkedList<Point>(
				internalLocations);
		if (internalLocations.isEmpty()) {
			internalLocations.add(location);
		} else {
			if (index < 0) {
				index = 0;
			} else if (index > internalLocations.size()) {
				index = internalLocations.size();
			}
			internalLocations.add(index, location);
		}
		propertyChangeSupport.firePropertyChange(
				Property.INTERNAL_LOCATIONS.name(), oldInternalLocations,
				internalLocations);
	}

	public void removeInternalLocation(Point location) {
		if (internalLocations.contains(location)) {
			LinkedList<Point> oldInternalLocations = new LinkedList<Point>(
					internalLocations);
			internalLocations.remove(location);
			propertyChangeSupport.firePropertyChange(
					Property.INTERNAL_LOCATIONS.name(), oldInternalLocations,
					internalLocations);
		}
	}

	public Point getStartAt() {
		return startAt;
	}

	public void setStartAt(Point startAt) {
		Point oldPoint = this.startAt;
		this.startAt = startAt;
		propertyChangeSupport.firePropertyChange(Property.START_AT.name(),
				oldPoint, this.startAt);
	}

	public Point getEndAt() {
		return endAt;
	}

	public void setEndAt(Point endAt) {
		Point oldPoint = this.endAt;
		this.endAt = endAt;
		propertyChangeSupport.firePropertyChange(Property.END_AT.name(),
				oldPoint, this.endAt);
	}

	public List<Point> getAllLocations() {
		List<Point> locations = new LinkedList<Point>();
		locations.add(startAt);
		locations.addAll(internalLocations);
		locations.add(endAt);
		return locations;
	}

	public List<Point> getInternalLocations() {
		return internalLocations;
	}

	public void setInternalLocations(List<Point> internalLocations) {
		LinkedList<Point> oldInternalLocations = new LinkedList<Point>(
				internalLocations);
		this.internalLocations = new LinkedList<Point>(internalLocations);
		propertyChangeSupport.firePropertyChange(
				Property.INTERNAL_LOCATIONS.name(), oldInternalLocations,
				internalLocations);
	}

	public LineStyle getLineStyle() {
		return lineStyle;
	}

	public void setLineStyle(LineStyle lineStyle) {
		LineStyle oldLineStyle = this.lineStyle;
		this.lineStyle = lineStyle;
		propertyChangeSupport.firePropertyChange(Property.LINE_STYLE.name(),
				oldLineStyle, this.lineStyle);
	}

	public ArrowType getStartArrowType() {
		return startArrowType;

	}

	public void setStartArrowType(ArrowType startArrowType) {
		ArrowType oldArrowType = this.startArrowType;
		this.startArrowType = startArrowType;
		propertyChangeSupport.firePropertyChange(
				Property.START_ARROW_TYPE.name(), oldArrowType,
				this.startArrowType);
	}

	public ArrowType getEndArrowType() {
		return endArrowType;
	}

	public void setEndArrowType(ArrowType endArrowType) {
		ArrowType oldArrowType = this.endArrowType;
		this.endArrowType = endArrowType;
		propertyChangeSupport.firePropertyChange(
				Property.END_ARROW_TYPE.name(), oldArrowType,
				this.endArrowType);
	}

}
